package com.example.springpethospital.controller;

import com.amazonaws.services.s3.model.CompleteMultipartUploadResult;
import com.example.springpethospital.common.exception.PetHospitalException;
import com.example.springpethospital.common.reponse.BaseResponse;
import com.example.springpethospital.common.reponse.EntityResponse;
import com.example.springpethospital.common.reponse.ResponseCodeEnum;
import com.example.springpethospital.common.service.BaseController;
import com.example.springpethospital.config.MinioConfig;
import com.example.springpethospital.entity.file.dto.UploadTaskCreateDTO;
import com.example.springpethospital.entity.file.dto.UploadTaskDTO;
import com.example.springpethospital.entity.file.param.UploadTaskCreateParam;
import com.example.springpethospital.service.FileService;
import com.example.springpethospital.service.MinioClientService;
import io.minio.GetObjectArgs;
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.MinioClient;
import io.minio.http.Method;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import javax.annotation.Resource;
import javax.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
@RequestMapping("/api/file")
public class FileController extends BaseController {

    @Resource
    private MinioClientService minioClientService;
    @Resource
    private FileService fileService;
    @Resource
    private MinioConfig minioConfig;


    private MinioClient getMinioClient() {
        return minioClientService.getMinioClient();
    }

    /**
     * 直接下载文件，高危，不建议使用
     */
    @GetMapping("/download/{bucket}/{fileName}")
    public BaseResponse download(@PathVariable("bucket") String bucket, @PathVariable("fileName") String fileName) {
        InputStream is = null;
        try {
            is = getMinioClient().getObject(GetObjectArgs.builder().bucket(bucket).object(fileName).build());
            IOUtils.copy(is, response.getOutputStream());
            response.setHeader("Content-Disposition", "attachment; filename=" + new String(fileName.getBytes(StandardCharsets.UTF_8), "ISO8859-1"));
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.addHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "Content-Disposition");
        } catch (Exception e) {
            e.printStackTrace();
            throw new PetHospitalException(e);
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    log.error(e.getMessage());
                }
            }
        }
        return BaseResponse.success();
    }

    /**
     * 获取文件下载地址，推荐下载使用
     */
    @GetMapping("/download/getDownloadUrl")
    public EntityResponse<String> downloadUrl(@RequestParam("fileName") String fileName) {
        GetPresignedObjectUrlArgs build = GetPresignedObjectUrlArgs.builder()
            .method(Method.GET)
            .bucket(minioConfig.getBucket())
            .object(fileName)
            // 生成的预签名url可访问的有效时间，最大期限7天
            .expiry(60 * 60 * 24)
            .build();
        try {
            String presignedObjectUrl = getMinioClient().getPresignedObjectUrl(build);
            return new EntityResponse<>(ResponseCodeEnum.SUCCESS, "success", presignedObjectUrl);
        } catch (Exception e) {
            e.printStackTrace();
            throw new PetHospitalException(e);
        }
    }

    /**
     * 创建一个上传任务
     */
    @PostMapping("/upload/task/create")
    public EntityResponse<UploadTaskCreateDTO> createUploadTask(@Valid @RequestBody UploadTaskCreateParam param, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return new EntityResponse<>(ResponseCodeEnum.FORBIDDEN, bindingResult.getFieldError().getDefaultMessage(), null);
        }
        UploadTaskCreateDTO uploadTaskCreateDTO = fileService.createUploadTask(param);
        return new EntityResponse<>(ResponseCodeEnum.SUCCESS, "success", uploadTaskCreateDTO);
    }

    /**
     * 获取上传进度
     *
     * @param identifier 文件md5
     */
    @GetMapping("/upload/task/schedule/{identifier}")
    public EntityResponse<UploadTaskDTO> queryUploadSchedule(@PathVariable("identifier") String identifier) {
        UploadTaskDTO uploadTaskDTO = fileService.queryUploadSchedule(identifier);
        return new EntityResponse<>(ResponseCodeEnum.SUCCESS, "success", uploadTaskDTO);
    }

    /**
     * 获取每个分片的预签名上传地址
     *
     * @param identifier md5
     * @param partNumber 分片number
     * @return
     */
    @GetMapping("/upload/task/signUploadUrl/{identifier}/{partNumber}")
    public EntityResponse<String> getPerSignUploadUrl(@PathVariable("identifier") String identifier, @PathVariable("partNumber") Integer partNumber) {
        UploadTaskDTO uploadTaskDTO = fileService.queryUploadSchedule(identifier);
        if (uploadTaskDTO == null) {
            throw new PetHospitalException("分片任务不存在");
        }
        String perSignUploadUrl = fileService.getPerSignUploadUrl(uploadTaskDTO.getBucketName(), uploadTaskDTO.getObjectKey(),
            uploadTaskDTO.getUploadId(), partNumber);
        return new EntityResponse<>(ResponseCodeEnum.SUCCESS, "success", perSignUploadUrl);
    }

    /**
     * 合并分片
     */
    @PostMapping("/upload/task/merge/{identifier}")
    public EntityResponse<CompleteMultipartUploadResult> merge(@PathVariable("identifier") String identifier) {
        return new EntityResponse<>(ResponseCodeEnum.SUCCESS, "success", fileService.merge(identifier));
    }
}
